package com.smsc.headend.task.engine.handler.impl.collection.builder;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.constant.DeviceMfg;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.enums.PhaseMeterWasion;
import com.smsc.headend.common.enums.SinglePhaseMeterWisdom;
import com.smsc.headend.common.enums.ThreePhaseMeterWisdom;
import com.smsc.headend.common.enums.errorcode.TaskHandlerErrorCode;
import com.smsc.headend.common.utils.CustomStringUtils;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.dto.MeterPhaseDTO;
import com.smsc.headend.module.asset.entity.DeviceTypeGroup;
import com.smsc.headend.module.data.entity.RdScheduleReadParamValue;
import com.smsc.headend.module.mdm.entity.LogMdmSync;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.task.consts.TaskKafkaTopic;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.RdScheduleReadParamValueService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 相位识别Handler
 *
 * @author zhou
 */
@Slf4j
@Service
public class PhaseRecognitionResultHandler implements ScheduleReadBuilder {

    @Autowired
    private FeignAssetManagementService assetManagementService;

    @Autowired
    private RdScheduleReadParamValueService rdScheduleReadParamValueService;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    AssetService assetService;


    /**
     * 威思顿表相关参数
     */
    private static final Integer MFG_SN_INDEX_WISDOM = 0;
    private static final String MFG_SN_TYPE_WISDOM = "OctetString";
    private static final Integer PHASE_INDEX_WISDOM = 1;
    private static final String PHASE_TYPE_WISDOM = "UInt16";

    /**
     * 威胜表相关参数(默认)
     */
    private static final Integer MFG_SN_INDEX = 0;
    private static final String MFG_SN_TYPE = "String";
    private static final Integer PHASE_INDEX = 7;
    private static final String PHASE_TYPE = "Enum";


    /**
     * 单相表
     */
    private static final String SINGLE_PHASE = "0";

    /**
     * 三相表
     */
    private static final String THERE_PHASE = "1";

    /**
     * 线路异常
     */
    private static final String LINE_EXCEPTION = "1";


    /**
     * 处理读取结果
     *
     * @param task
     * @param atomicTask
     * @param deviceData
     * @param actionResult
     * @param dcuDataItemFlag
     */
    @Override
    public void handleScheduleReadResult(Task task, AtomicTask atomicTask, DeviceData deviceData, String actionResult, Boolean dcuDataItemFlag, Map<String, Object> param) throws UDISTaskHandlerException {

        String referValue = (String) param.get("referValue");
        Long dataItemId = Convert.toLong(param.get("dataItemId"));

        List<MeterPhaseDTO> meterPhaseDTOListFromMeter = new ArrayList<>();
        List<String> mfgSNList = new ArrayList<>();

        //需要更新的相位信息
        List<MeterPhaseDTO> meterPhaseDTOListToUpdate = new ArrayList<>();
        //需要同步MDM的相位信息
        List<MeterPhaseDTO> meterPhaseDTOListToSync = new ArrayList<>();

        String consistent = assetService.getSystemProperty("CONSISTENT_DEVICE_PROTOCOL_ENABLE");
        if (StringUtils.isEmpty(consistent) || Boolean.valueOf(consistent).equals(Boolean.FALSE)) {
            translatePhaseOld(task, deviceData, mfgSNList, meterPhaseDTOListFromMeter);
        } else {
            translatePhase(task, deviceData, mfgSNList, meterPhaseDTOListFromMeter);
        }
        log.debug("meterPhaseDTOListFromMeter :" + JSONUtil.toJsonStr(meterPhaseDTOListFromMeter));

        List<MeterPhaseDTO> meterPhaseDTOListFromDb = assetManagementService.getMeterListByMfgSnList(mfgSNList);
        if (CollectionUtil.isEmpty(meterPhaseDTOListFromDb)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.UNABLE_GET_MFGSN_LIST_IN_DB + JSONUtil.toJsonStr(mfgSNList));
        }
        log.debug("meterPhaseDTOListFromDb :" + JSONUtil.toJsonStr(meterPhaseDTOListFromDb));

        savePhase(referValue, dataItemId, meterPhaseDTOListFromMeter, meterPhaseDTOListToUpdate, meterPhaseDTOListToSync, meterPhaseDTOListFromDb);

        //更新数据库
        if (CollUtil.isNotEmpty(meterPhaseDTOListToUpdate)) {
            assetManagementService.updateMeterPhase(meterPhaseDTOListToUpdate);
        }
        //同步MDM
        if (CollUtil.isNotEmpty(meterPhaseDTOListToSync)) {
            kafkaTemplate.send(TaskKafkaTopic.CLIENT_PHASE_CHANGED, JSONUtil.toJsonStr(meterPhaseDTOListToSync).getBytes(StandardCharsets.UTF_8));
        }

    }

    /**
     * 解析相位信息--旧的
     *
     * @param task
     * @param deviceData
     * @param mfgSNList
     * @param meterPhaseDTOListFromMeter
     */
    public void translatePhaseOld(Task task, DeviceData deviceData, List<String> mfgSNList, List<MeterPhaseDTO> meterPhaseDTOListFromMeter) {
        Integer mfgSnIndex = MFG_SN_INDEX;
        String mfgSnType = MFG_SN_TYPE;
        Integer phaseIndex = PHASE_INDEX;
        String phaseType = PHASE_TYPE;
        Integer manufacturerId = 0;
        //wisdom表计特殊处理
        ComConfigDMO comConfigDMO = assetService.getComConfigById(task.getComId());
        if (null != comConfigDMO) {
            DeviceTypeGroup dtg = redisUtils.getDeviceTypeGroup(comConfigDMO.getModelId(), null, assetManagementService);

            if (null != dtg && dtg.getManufacturerId().equals(DeviceMfg.MF_Wisdom)) {
                log.debug("PhaseRecognitionResultHandler DeviceTypeGroup: {}" + JSONUtil.toJsonStr(dtg));
                mfgSnIndex = MFG_SN_INDEX_WISDOM;
                mfgSnType = MFG_SN_TYPE_WISDOM;
                phaseIndex = PHASE_INDEX_WISDOM;
                phaseType = PHASE_TYPE_WISDOM;
                manufacturerId = DeviceMfg.MF_Wisdom;
            }
        }

        CosemData cosemData = (CosemData) deviceData;

        Integer finalMfgSnIndex = mfgSnIndex;
        String finalMfgSnType = mfgSnType;
        Integer finalPhaseIndex = phaseIndex;
        String finalPhaseType = phaseType;
        Integer finalManufacturerId = manufacturerId;

        cosemData.getChild().stream().forEach(structData -> {
            MeterPhaseDTO meterPhaseDTO = new MeterPhaseDTO();
            String mfgSN = "";
            String encryptedMfgSn = null;
            String phase = null;
            for (int index = 0; index < structData.getChild().size(); index++) {

                CosemData data = structData.getChild().get(index);
                if (index == finalMfgSnIndex && finalMfgSnType.equals(data.getType())) {
                    try {
                        encryptedMfgSn = data.getValue();
                        if (finalManufacturerId.equals(DeviceMfg.MF_Wisdom)) {
                            mfgSN = HexUtil.decodeHexStr(encryptedMfgSn);
                        } else {
                            mfgSN = encryptedMfgSn;
                        }
                        meterPhaseDTO.setMfgSN(mfgSN);
                    } catch (Exception e) {
                        log.error("data cannot be successfully parse to mfgSn:" + JSONUtil.toJsonStr(data));
                        continue;
                    }
                }

                if (index == finalPhaseIndex && finalPhaseType.equals(data.getType())) {
                    try {
                        phase = getPhaseByMeterValueOld(data.getValue(), finalManufacturerId);
                        meterPhaseDTO.setPhase(phase);
                    } catch (Exception e) {
                        log.error("data cannot be successfully parse to phase:" + JSONUtil.toJsonStr(data));
                        continue;
                    }
                }
                if (StrUtil.isNotEmpty(mfgSN) && null != phase) {
                    mfgSNList.add(mfgSN);
                    meterPhaseDTOListFromMeter.add(meterPhaseDTO);
                }
            }
        });
    }

    /**
     * 解析相位信息 --澳电项目威胜和威思顿的协议一致，无需区分
     *
     * @param task
     * @param deviceData
     * @param mfgSNList
     * @param meterPhaseDTOListFromMeter
     */
    public void translatePhase(Task task, DeviceData deviceData, List<String> mfgSNList, List<MeterPhaseDTO> meterPhaseDTOListFromMeter) {
        Integer mfgSnIndex = MFG_SN_INDEX_WISDOM;
        String mfgSnType = MFG_SN_TYPE_WISDOM;
        Integer phaseIndex = PHASE_INDEX_WISDOM;
        String phaseType = PHASE_TYPE_WISDOM;
        CosemData cosemData = (CosemData) deviceData;


        cosemData.getChild().stream().forEach(structData -> {
            MeterPhaseDTO meterPhaseDTO = new MeterPhaseDTO();
            String mfgSN = "";
            String encryptedMfgSn = null;
            String phase = null;
            for (int index = 0; index < structData.getChild().size(); index++) {

                CosemData data = structData.getChild().get(index);
                if (index == mfgSnIndex && mfgSnType.equals(data.getType())) {
                    try {
                        encryptedMfgSn = data.getValue();
                        mfgSN = HexUtil.decodeHexStr(encryptedMfgSn);
                        meterPhaseDTO.setMfgSN(mfgSN);
                    } catch (Exception e) {
                        log.error("data cannot be successfully parse to mfgSn:" + JSONUtil.toJsonStr(data));
                        continue;
                    }
                }

                if (index == phaseIndex && phaseType.equals(data.getType())) {
                    try {
                        phase = getPhaseByMeterValue(data.getValue());
                        meterPhaseDTO.setPhase(phase);
                    } catch (Exception e) {
                        log.error("data cannot be successfully parse to phase:" + JSONUtil.toJsonStr(data));
                        continue;
                    }
                }
                if (StrUtil.isNotEmpty(mfgSN) && null != phase) {
                    mfgSNList.add(mfgSN);
                    meterPhaseDTOListFromMeter.add(meterPhaseDTO);
                }
            }
        });
    }

    /**
     * 保存相位信息
     *
     * @param referValue
     * @param dataItemId
     * @param meterPhaseDTOListFromMeter
     * @param meterPhaseDTOListToUpdate
     * @param meterPhaseDTOListToSync
     * @param meterPhaseDTOListFromDb
     */
    public void savePhase(String referValue, Long dataItemId, List<MeterPhaseDTO> meterPhaseDTOListFromMeter, List<MeterPhaseDTO> meterPhaseDTOListToUpdate, List<MeterPhaseDTO> meterPhaseDTOListToSync, List<MeterPhaseDTO> meterPhaseDTOListFromDb) {
        List<RdScheduleReadParamValue> rdScheduleReadParamValueList = new ArrayList<>();
        meterPhaseDTOListFromMeter.stream().forEach(elementFromMeter -> {
            MeterPhaseDTO meterPhaseFromDb =
                    meterPhaseDTOListFromDb.stream().filter(elementFromDb -> elementFromDb.getMfgSN().equals(elementFromMeter.getMfgSN())).findFirst().orElse(null);
            if (null == meterPhaseFromDb) {
                log.debug("unable to get the meter record by the mfgSn:" + elementFromMeter.getMfgSN());
                return;
            }

            RdScheduleReadParamValue rdScheduleReadParamValue = new RdScheduleReadParamValue();
            rdScheduleReadParamValue.setArchiveValue(meterPhaseFromDb.getPhase());
            rdScheduleReadParamValue.setDeviceId(meterPhaseFromDb.getMeterId());
            rdScheduleReadParamValue.setDeviceType(DeviceType.METER.getId());
            rdScheduleReadParamValue.setDeviceValue(elementFromMeter.getPhase());
            rdScheduleReadParamValue.setTv(Long.valueOf(Math.toIntExact(SystemClock.now() / 1000)));
            rdScheduleReadParamValue.setTvCreate(Long.valueOf(Math.toIntExact(SystemClock.now() / 1000)));
            rdScheduleReadParamValue.setReferValue(referValue);
            rdScheduleReadParamValue.setSubDataItemId(dataItemId);
            rdScheduleReadParamValue.setSuggestResult(getSuggest(referValue, elementFromMeter.getPhase()));
            rdScheduleReadParamValueList.add(rdScheduleReadParamValue);

            MeterPhaseDTO meterPhaseDTO = new MeterPhaseDTO();
            meterPhaseDTO.setCustomerSN(meterPhaseFromDb.getCustomerSN());
            meterPhaseDTO.setMeterId(meterPhaseFromDb.getMeterId());
            meterPhaseDTO.setMfgSN(meterPhaseFromDb.getMfgSN());
            meterPhaseDTO.setPhase(elementFromMeter.getPhase());
            meterPhaseDTO.setTv(SystemClock.now() / 1000);

            // 如果相位为UNKNOWN,UDIS不入库结果，不同步MDM。
            if (StrUtil.isNotEmpty(elementFromMeter.getPhase()) && !PhaseMeterWasion.UNKNOWN.getPhaseName().equalsIgnoreCase(elementFromMeter.getPhase())) {
                if (!elementFromMeter.getPhase().equals(meterPhaseFromDb.getPhase())) {
                    meterPhaseDTOListToUpdate.add(meterPhaseDTO);
                    meterPhaseDTOListToSync.add(meterPhaseDTO);
                } else {
                    LogMdmSync lastMdmSyncLog = assetManagementService.getLastMdmSyncLog(meterPhaseFromDb.getMeterId(), DeviceType.METER.getId(), LogMdmSync.SyncType.PHASE.getValue(), elementFromMeter.getPhase());
                    if (lastMdmSyncLog == null || lastMdmSyncLog.getSyncResult() == LogMdmSync.Result.FAILED.getCode()) {
                        meterPhaseDTOListToUpdate.add(meterPhaseDTO);
                        meterPhaseDTOListToSync.add(meterPhaseDTO);
                    }
                }
            }
        });

        log.debug("rdScheduleReadParamValueList to save is : {}, meterPhaseDTOListToUpdate to update is :{}, meterPhaseDTOListToSync to syncMdm is : {}",
                rdScheduleReadParamValueList, meterPhaseDTOListToUpdate, meterPhaseDTOListToSync);
        rdScheduleReadParamValueService.saveBatch(rdScheduleReadParamValueList);
    }


    /**
     * 获取不同表计的相位信息-旧的，nesco项目
     *
     * @param value
     * @param manufacturerId
     * @return
     */
    public String getPhaseByMeterValueOld(String value, Integer manufacturerId) {
        String phase = "";
        if (StringUtils.isNotBlank(value)) {
            // 1.威思顿表计
            if (manufacturerId.equals(DeviceMfg.MF_Wisdom)) {
                if (value.length() == 4) {
                    value = value.substring(0, 2);
                }
                value = CustomStringUtils.hexStringToBitString(value);
                //判断单相表或多项表,D3:0 表示单相表、1 表示三相表
                String type = value.substring(4, 5);
                //线路异常状态1:线路异常，0:无异常或不支持识别功能
                String exception = value.substring(3, 4);
                //三相表
                if (THERE_PHASE.equals(type)) {
                    //D2D1D0全为0，UNKNOWN，D2D1D0其中有一个为0，DEVICE_VALUE按“断相”入库,D2D1D0均为1，DEVICE_VALUE按D7D6D5解析入库
                    String ddd = value.substring(5, 8);
                    value = value.substring(0, 3);
                    String unKnow = "000";
                    String loss = "0";
                    if (unKnow.equals(ddd)) {
                        phase = ThreePhaseMeterWisdom.UNKNOWN.getPhaseName();
                    } else if (ddd.contains(loss)) {
                        phase = ThreePhaseMeterWisdom.LOSS.getPhaseName();
                    } else {
                        phase = ThreePhaseMeterWisdom.getById(value).getPhaseName();
                    }
                }
                //单相表
                if (SINGLE_PHASE.equals(type)) {
                    //是否线路异常
                    value = value.substring(5, 8);
                    if (LINE_EXCEPTION.equals(exception)) {
                        String tempPhase = SinglePhaseMeterWisdom.getById(value).getPhaseName();
                        if (!SinglePhaseMeterWisdom.UNKNOWN.getPhaseName().equals(tempPhase)) {
                            phase = SinglePhaseMeterWisdom.getById(value).getPhaseName() + ":" + SinglePhaseMeterWisdom.REVERSE.getPhaseName();
                        } else {
                            phase = SinglePhaseMeterWisdom.getById(value).getPhaseName();
                        }
                    } else {
                        phase = SinglePhaseMeterWisdom.getById(value).getPhaseName();
                    }
                }
            }
            // 2.威胜及其他表计
            else {
                phase = PhaseMeterWasion.getById(value).getPhaseName();
            }
        }
        return phase;
    }

    /**
     * 获取不同表计的相位信息,澳电项目威胜和威思顿的协议一致
     *
     * @param value
     * @return
     */
    public String getPhaseByMeterValue(String value) {
        String phase = "";
        if (StringUtils.isNotBlank(value)) {
            // 1.威思顿表计和威胜表计协议一致
            if (value.length() == 4) {
                value = value.substring(0, 2);
            }
            value = CustomStringUtils.hexStringToBitString(value);
            //判断单相表或多项表,D3:0 表示单相表、1 表示三相表
            String type = value.substring(4, 5);
            //线路异常状态1:线路异常，0:无异常或不支持识别功能
            String exception = value.substring(3, 4);
            //三相表
            if (THERE_PHASE.equals(type)) {
                //D2D1D0全为0，UNKNOWN，D2D1D0其中有一个为0，DEVICE_VALUE按“断相”入库,D2D1D0均为1，DEVICE_VALUE按D7D6D5解析入库
                String ddd = value.substring(5, 8);
                value = value.substring(0, 3);
                String unKnow = "000";
                String loss = "0";
                if (unKnow.equals(ddd)) {
                    phase = ThreePhaseMeterWisdom.UNKNOWN.getPhaseName();
                } else if (ddd.contains(loss)) {
                    phase = ThreePhaseMeterWisdom.LOSS.getPhaseName();
                } else {
                    phase = ThreePhaseMeterWisdom.getById(value).getPhaseName();
                }
            }
            //单相表
            if (SINGLE_PHASE.equals(type)) {
                //是否线路异常
                value = value.substring(5, 8);
                if (LINE_EXCEPTION.equals(exception)) {
                    String tempPhase = SinglePhaseMeterWisdom.getById(value).getPhaseName();
                    if (!SinglePhaseMeterWisdom.UNKNOWN.getPhaseName().equals(tempPhase)) {
                        phase = SinglePhaseMeterWisdom.getById(value).getPhaseName() + ":" + SinglePhaseMeterWisdom.REVERSE.getPhaseName();
                    } else {
                        phase = SinglePhaseMeterWisdom.getById(value).getPhaseName();
                    }
                } else {
                    phase = SinglePhaseMeterWisdom.getById(value).getPhaseName();
                }
            }
        }
        return phase;
    }
}
